using System.Collections.Generic;
using System.Linq;
using GameLogic;

namespace GameLogic.Poker
{
    public class Hand
    {
        private HandRang? rang;
        private Rang? highCard;
        protected bool Equals(Hand other)
        {
            return Cards.Count == other.Cards.Count && Cards.All(c => other.Cards.Contains(c));
        }

        public override int GetHashCode()
        {
            return (Cards != null ? Cards.GetHashCode() : 0);
        }

        public List<Card> Cards { get; set; }


        public Hand()
        {
            Cards=new List<Card>(5);
        }

        public Hand(IEnumerable<Card> cards)
        {
            Cards = new List<Card>(cards);
        }

        public Hand(params Card[] cards)
        {
            Cards = new List<Card>(cards);
        }

        public override bool Equals(object obj)
        {
            if (ReferenceEquals(null, obj)) return false;
            if (ReferenceEquals(this, obj)) return true;
            if (obj.GetType() != this.GetType()) return false;
            return Equals((Hand)obj);
        }


        public HandRang HandRang
        {
            get { return (HandRang)(rang ?? (rang = getHandRang())); }//Do we realy need here '=' ???
        }

        public Rang HighCard
        {
            get
            {
                return (Rang)(highCard ?? (highCard = getHighCardRang()));//Do we realy need here '=' ???
            }
        }
        private HandRang getHandRang()
        {
            int card;
            HandRang res = WhatHand(out card);
            highCard = (Rang)card;
            return res;
        }
        private Rang getHighCardRang()
        {
            int card;
            HandRang res = WhatHand(out card);
            rang = res;
            return (Rang)card;
        }
        private HandRang WhatHand(out int Card)
        {
            Card = 0;
            if (Cards == null)
                return 0;
            if (Cards.Count != 5)
                return 0;
            //Sort cards in hand for better search of combination
            Cards.Sort((card, card1) => card.Rang.CompareTo(card1.Rang));
            //Bad costruction, need to refactor
            /*if (IsRoyalFlush(out Card))
                return HandRang.RoyalFlush;*/
            if (IsStraightFlush(out Card))
                return HandRang.StraightFlush;
            if (IsQuads(out Card))
                return HandRang.FourOfAKind;
            if (IsFullHouse(out Card))
                return HandRang.FullHouse;
            if (IsFlush(out Card))
                return HandRang.Flush;
            if (IsStraight(out Card))
                return HandRang.Straight;
            if (IsThreeOfAKind(out Card))
                return HandRang.ThreeOfAKind;
            if (IsTwoPair(out Card))
                return HandRang.TwoPair;
            if (IsOnePair(out Card))
                return HandRang.OnePair;
            //default HighCard
            Card = (int)Cards[4].Rang;
            return HandRang.HighCard;
        }


        //private int IsRoyalFlush()
        /*private bool IsRoyalFlush(out int highCard)
        {
            highCard = 0;
            if (Cards[4].Rang != Rang.Ace)
                return false;
            if (Cards.Any(card => card.Suit != Cards[0].Suit))
                return false;
            for (int i = 1; i < 5; i++)
            {
                if ((int)Cards[i].Rang != ((int)Cards[i - 1].Rang + 1))
                    return false;
            }
            highCard = (int)Rang.Ace;
            return true;

        }*/
        private bool IsStraightFlush(out int highCard)
        {
            highCard = 0;
            if (Cards.Any(card => card.Suit != Cards[0].Suit))
                return false;
            for (int i = 1; i < 5; i++)
            {
                if ((int)Cards[i].Rang != ((int)Cards[i - 1].Rang + 1))
                    return false;
            }
            highCard = (int)Cards[4].Rang;
            return true;
        }
        private bool IsQuads(out int highCard)
        {
            //return Cards.GroupBy(c => c.Rang).Any(x => x.Count() == 4);
            if ((Cards[4].Rang == Cards[3].Rang) && (Cards[4].Rang == Cards[2].Rang) &&
                (Cards[4].Rang == Cards[1].Rang))
            {
                highCard = (int)Cards[4].Rang;
                return true;
            }
            if ((Cards[3].Rang == Cards[2].Rang) && (Cards[3].Rang == Cards[1].Rang) &&
                (Cards[3].Rang == Cards[0].Rang))
            {
                highCard = (int)Cards[3].Rang;
                return true;
            }
            highCard = 0;
            return false;
        }
        private bool IsFullHouse(out int highCard)
        {
            //return Cards.GroupBy(c => c.Rang).Where(x => x.Count() >= 2).Count() == 2;

            if ((Cards[4].Rang == Cards[3].Rang) && (Cards[4].Rang == Cards[2].Rang) && //three euqal
                (Cards[1].Rang == Cards[0].Rang))//pair
            {
                highCard = (int)Cards[4].Rang;
                return true;
            }
            if ((Cards[2].Rang == Cards[1].Rang) && (Cards[2].Rang == Cards[0].Rang) && //three equal
                (Cards[4].Rang == Cards[3].Rang))//pair
            {
                highCard = (int)Cards[2].Rang;
                return true;
            }
            highCard = 0;
            return false;
        }
        private bool IsFlush(out int highCard)
        {
            //return Cards.GroupBy(c => c.Suit).Count() == 1;

            if (Cards.Any(card => card.Suit != Cards[0].Suit))
            {
                highCard = 0;
                return false;
            }
            highCard = (int)Cards[4].Rang;
            return true;
        }
        private bool IsStraight(out int highCard)
        {
            /*if (Cards.Any(card => card.Rang == Rang.Ace))
            {
                highCard = (int)Rang.Ace; //So here we have a problems, cause Ace may begin or end sequence of Straight
                return true;
            }*/
            for (int i = 1; i < 5; i++)
                if ((int)Cards[i].Rang != ((int)Cards[i - 1].Rang + 1))
                {
                    highCard = 0;
                    return false;
                }
            highCard = (int)Cards[4].Rang;
            return true;
        }
        private bool IsThreeOfAKind(out int highCard)
        {
            highCard = (int)Cards[2].Rang;
            return (Cards.GroupBy(c => c.Rang).Any(x => x.Count() == 3));
            /*for (int i = 4; i > 1; i--)  <--does not work
            {
                if (((int)Cards[i].Rang == ((int)Cards[i - 1].Rang + 1)) &&
                    ((int)Cards[i].Rang == ((int)Cards[i - 2].Rang + 2)))
                {
                    highCard = (int) Cards[i].Rang;
                    return true;
                }
            }
            highCard = 0;
            return false;*/
        }
        private bool IsTwoPair(out int highCard)
        {
            //return !IsFullHouse() && //+ from full house

            int flag = -1;
            for (int i = 4; i > 0; i--)
            {
                if (Cards[i].Rang == Cards[i - 1].Rang)
                    if (flag != -1)
                    {
                        highCard = (int)Cards[flag].Rang;
                        return true;
                    }
                    else
                        flag = i;
            }
            highCard = 0;
            return false;
        }
        private bool IsOnePair(out int highCard)
        {
            //Cards.GroupBy(c => c.Rang).Any(x => x.Count() == 2);

            for (int i = 4; i > 0; i--)
            {
                if (Cards[i].Rang == Cards[i - 1].Rang)
                {
                    highCard = (int)Cards[i].Rang;
                    return true;
                }
            }
            highCard = 0;
            return false;
        }
    }
}